package grape.common.rest.mvc;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import grape.common.AbstractLoginUser;
import grape.common.exception.ExceptionTools;
import grape.common.exception.runtime.RBaseException;
import grape.common.rest.form.BasePageForm;
import grape.common.service.common.IBaseService;
import grape.common.service.common.dataconstraint.BaseDataConstraintHelper;
import grape.common.service.common.dataconstraint.ConstraintCompiledContent;
import grape.common.service.common.dataconstraint.IDataObject;
import grape.common.service.po.IDBasePo;
import grape.common.service.query.BasePoQuery;
import grape.common.service.update.BaseUpdateWithNullCondition;
import grape.common.tools.ThreadContextTool;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.catalina.users.AbstractUser;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 正常业务实体的controller基类,提供一些通用方法
 * @param <Vo> 简单vo
 * @param <Po> po
 */
@Data
@EqualsAndHashCode(callSuper=false)
public abstract class BaseController<Vo,Po extends IDBasePo<?,?>> extends SuperController {

    public static final String enableDefaultDataObjectKey = "enableDefaultDataObjectKey";

    @Autowired
    private IBaseService<Po> service;

    @Autowired
    private WebMapper<Vo,Po> mapperConverter;

    @Autowired(required = false)
    private BaseDataConstraintHelper baseDataConstraintHelper;

    /**
     * 单表添加
     * @param poQuery
     * @return
     */
    public Vo create(Po poQuery){
        Po dbPo = service.insert(poQuery);
        return returnCreate(dbPo);
    }

    /**
     * 单表根据id获取
     * @param id
     * @return
     */

    public Vo queryById(String id){
        Po dbPo = service.getById(id);
        Vo vo = mapperConverter.poToVo(dbPo);
        if (vo == null) {
            throw ExceptionTools.dataNotExistRE(null);
        }
        vo = transVo(vo);
        return vo;
    }


    /**
     * 单表根据id删除
     * @param id
     * @return
     */

    public boolean deleteById(String id){
        boolean r = service.removeById(id);
        if (!r) {
            throw ExceptionTools.dataNotExistRE("数据不存在，请刷新后再试");
        }
        // 如果这里返回null，并不会被GlobalResponseBodyAdvice 统一包装
        return r;
    }

    /**
     * 单表非全字段更新
     * @param poQuery
     * @return
     */

    public Vo update(Po poQuery){
        return update(poQuery, false);
    }

    /**
     * 单表更新
     * @param poQuery
     * @param isFull 是否全字段更新
     * @return
     */
    public Vo update(Po poQuery,boolean isFull){
        boolean r;
        if (isFull) {
            r = service.updateFullById(poQuery);
        }else {
            r = service.updateById(poQuery);
        }
        if (!r) {
            throw ExceptionTools.failRE("更新失败，请刷新数据后再试");
        }
        return queryById(poQuery.getId().toString());
    }

    /**
     * 单表更新，有选择的将某个字段设置为null
     * @param poQuery
     * @return
     */
    public Vo update(BaseUpdateWithNullCondition<Po> poQuery){
        boolean r = service.updateById(poQuery);
        if (!r) {
            throw ExceptionTools.failRE("更新失败，请刷新数据后再试");
        }
        return queryById(poQuery.getPo().getId().toString());
    }

    /**
     * 分页查询
     * @param poQuery
     * @param pageForm
     * @return
     */
    public IPage<Vo> listPage(Po poQuery, BasePageForm pageForm){
        IPage<Po> page = service.page(new Page(pageForm.getCurrent(),pageForm.getSize()),poQuery);
        if (page.getTotal() == 0) {
            throw ExceptionTools.dataNotExistRE("暂无数据");
        }
        return pagePoToVo(page);

    }
    /**
     * 分页查询,带额外条件
     * @param poQuery
     * @param pageForm
     * @return
     */
    public IPage<Vo> listPage(BasePoQuery<Po> poQuery, BasePageForm pageForm){
        IPage<Po> page = service.page(new Page(pageForm.getCurrent(),pageForm.getSize()),poQuery);
        if (page.getTotal() == 0) {
            throw ExceptionTools.dataNotExistRE("暂无数据");
        }
        return pagePoToVo(page);

    }


    /**
     * 不分页查询
     * @param poQuery
     * @return
     */
    public List<Vo> list(Po poQuery){
        List list = service.list(poQuery);
        return posToVos(list);
    }

    /**
     * 不分页查询，带额外参数
     * @param poQuery
     * @return
     */
    public List<Vo> list(BasePoQuery<Po> poQuery){
        List list = service.list(poQuery);
        return posToVos(list);
    }

    /**
     * 用来返回添加后的响应
     * @param dbPo
     * @return
     */
    protected Vo returnCreate(Po dbPo){
        if (dbPo == null) {
            throw ExceptionTools.newRE("添加失败");
        }
        Vo vo = mapperConverter.poToVo(dbPo);
        vo = transVo(vo);
        return vo;
    }

    /**
     * pos转vos
     * @param pos
     * @return
     */
    public List<Vo> posToVos(List<Po> pos) {
        return pos.stream().map(po->transVo(mapperConverter.poToVo(po))).collect(Collectors.toList());
    }

    /**
     * 分页po转vo
     * @param page
     * @return
     */
    public IPage<Vo> pagePoToVo(IPage page){
        List<Po> records = page.getRecords();
        if (page != null && !isEmpty(records)) {
            page.setRecords(posToVos(records));
            return page;
        }
        // 原样返回page
        return page;
    }
    /**
     * 翻译vo的额外属性，如：字典id换名称，但一般提供了翻译注解，除非特别复杂的翻译直接手动写，否则直接实现ITransService接口即可
     * @param vo
     * @return
     */
    public Vo transVo(Vo vo){
        return vo;
    }


    /****************************** 以是数据约束相关 ****************************************************/

    /**
     * 单表根据id获取，重载数据对象约束
     * @param id
     * @return
     */

    public Vo queryById(String id,String dataObjectCode){
        return queryById(id, parseConstraint(dataObjectCode));
    }
    /**
     * 单表根据id获取，重载数据对象约束
     * @param id
     * @return
     */

    public Vo queryById(String id,IDataObject dataObject){
        return queryById(id, parseConstraint(dataObject));
    }
    /**
     * 单表根据id获取，重载数据对象约束
     * @param id
     * @return
     */

    public Vo queryById(String id,List<ConstraintCompiledContent> constraintContent){
        Po dbPo = getService().getById(id,constraintContent);
        Vo vo = getMapperConverter().poToVo(dbPo);
        if (vo == null) {
            throw ExceptionTools.dataNotExistRE(null);
        }
        vo = transVo(vo);
        return vo;
    }

    /**
     * 单表根据id删除，重载数据对象约束
     * @param id
     * @return
     */

    public boolean deleteById(String id,String dataObjectCode){

        return deleteById(id, parseConstraint(dataObjectCode));
    }
    /**
     * 单表根据id删除，重载数据对象约束
     * @param id
     * @return
     */

    public boolean deleteById(String id,IDataObject dataObject){

        return deleteById(id, parseConstraint(dataObject));
    }
    /**
     * 单表根据id删除，重载数据对象约束
     * @param id
     * @return
     */

    public boolean deleteById(String id,List<ConstraintCompiledContent> constraintContent){

        boolean r = getService().removeById(id,constraintContent);
        if (!r) {
            throw ExceptionTools.dataNotExistRE("数据不存在，请刷新后再试");
        }
        // 如果这里返回null，并不会被GlobalResponseBodyAdvice 统一包装
        return r;
    }
    /**
     * 单表更新，重载数据对象约束
     * @param poQuery
     * @return
     */

    public Vo update(Po poQuery,String dataObjectCode){
        return update(poQuery, parseConstraint(dataObjectCode));
    }
    public Vo update(BaseUpdateWithNullCondition<Po> poQuery,String dataObjectCode){
        return update(poQuery, parseConstraint(dataObjectCode));
    }
    /**
     * 单表更新，重载数据对象约束
     * @param poQuery
     * @return
     */

    public Vo update(Po poQuery,IDataObject dataObject){
        return update(poQuery, parseConstraint(dataObject));
    }
    public Vo update(BaseUpdateWithNullCondition<Po> poQuery,IDataObject dataObject){
        return update(poQuery, parseConstraint(dataObject));
    }
    /**
     * 单表更新，重载数据对象约束
     * @param poQuery
     * @return
     */

    public Vo update(Po poQuery,List<ConstraintCompiledContent> constraintContent){
        boolean r = getService().updateById(poQuery,constraintContent);
        if (!r) {
            throw ExceptionTools.failRE("更新失败，请刷新数据后再试");
        }
        Vo vo = getMapperConverter().poToVo(getService().getById(poQuery.getId().toString()));
        vo = transVo(vo);
        return vo;
    }

    public Vo update(BaseUpdateWithNullCondition<Po> poQuery,List<ConstraintCompiledContent> constraintContent){
        boolean r = getService().updateById(poQuery,constraintContent);
        if (!r) {
            throw ExceptionTools.failRE("更新失败，请刷新数据后再试");
        }
        Vo vo = getMapperConverter().poToVo(getService().getById(poQuery.getPo().getId().toString()));
        vo = transVo(vo);
        return vo;
    }
    /**
     * 单表更新
     * @param poQuery
     * @param isFull 是否全字段更新
     * @return
     */
    public Vo update(Po poQuery,boolean isFull,List<ConstraintCompiledContent> constraintContent){
        boolean r;
        if (isFull) {
            r = service.updateFullById(poQuery,constraintContent);
        }else {
            r = service.updateById(poQuery,constraintContent);
        }
        if (!r) {
            throw ExceptionTools.failRE("更新失败，请刷新数据后再试");
        }
        return queryById(poQuery.getId().toString());
    }
    /**
     * 分页查询，重载数据对象约束
     * @param poQuery
     * @param pageForm
     * @return
     */
    public IPage<Vo> listPage(Po poQuery, BasePageForm pageForm,String dataObjectCode){
        assertParamNotEmpty(dataObjectCode,"数据对象编码不能为空");
        return listPage(poQuery, pageForm, parseConstraint(dataObjectCode));
    }

    /**
     * 分页查询，重载数据对象约束,可以带额外参数
     * @param poQuery
     * @param pageForm
     * @param dataObjectCode
     * @return
     */
    public IPage<Vo> listPage(BasePoQuery<Po> poQuery, BasePageForm pageForm,String dataObjectCode){
        assertParamNotEmpty(dataObjectCode,"数据对象编码不能为空");
        return listPage(poQuery, pageForm, parseConstraint(dataObjectCode));
    }
    /**
     * 分页查询，重载数据对象约束
     * @param poQuery
     * @param pageForm
     * @return
     */
    public IPage<Vo> listPage(Po poQuery, BasePageForm pageForm,IDataObject dataObject){
        assertNotNull(dataObject,"数据对象编码不能为空");
        return listPage(poQuery, pageForm, parseConstraint(dataObject));
    }

    /**
     * 分页查询，重载数据对象约束，可以带额外参数
     * @param poQuery
     * @param pageForm
     * @param dataObject
     * @return
     */
    public IPage<Vo> listPage(BasePoQuery<Po> poQuery, BasePageForm pageForm,IDataObject dataObject){
        assertNotNull(dataObject,"数据对象编码不能为空");
        return listPage(poQuery, pageForm, parseConstraint(dataObject));
    }
    /**
     * 分页查询，重载数据对象约束
     * @param poQuery
     * @param pageForm 主要是为了获取请求分页信息
     * @return
     */
    public IPage<Vo> listPage(Po poQuery, BasePageForm pageForm,List<ConstraintCompiledContent> constraintContent){
        assertNotNull(constraintContent,"约束内容不能为空");
        IPage<Po> page = getService().page(new Page(pageForm.getCurrent(),pageForm.getSize()),poQuery,constraintContent);
        if (page.getTotal() == 0) {
            throw ExceptionTools.dataNotExistRE("暂无数据");
        }
        return pagePoToVo(page);
    }

    /**
     * 分页查询，重载数据对象约束,可以带额外参数
     * @param poQuery
     * @param pageForm
     * @param constraintContent
     * @return
     */
    public IPage<Vo> listPage(BasePoQuery<Po> poQuery, BasePageForm pageForm, List<ConstraintCompiledContent> constraintContent){
        assertNotNull(constraintContent,"约束内容不能为空");
        IPage<Po> page = getService().page(new Page(pageForm.getCurrent(),pageForm.getSize()),poQuery,constraintContent);
        if (page.getTotal() == 0) {
            throw ExceptionTools.dataNotExistRE("暂无数据");
        }
        return pagePoToVo(page);
    }


    /**
     * 不分页查询，重载数据对象约束
     * @param poQuery
     * @return
     */
    public List<Vo> list(Po poQuery,String dataObjectCode){
        assertParamNotEmpty(dataObjectCode,"数据对象编码不能为空");
        return list(poQuery, parseConstraint(dataObjectCode));
    }

    /**
     * 不分页查询，重载数据对象约束,可以带额外参数
     * @param poQuery
     * @param dataObjectCode
     * @return
     */
    public List<Vo> list(BasePoQuery<Po> poQuery,String dataObjectCode){
        assertParamNotEmpty(dataObjectCode,"数据对象编码不能为空");
        return list(poQuery, parseConstraint(dataObjectCode));
    }
    /**
     * 不分页查询，重载数据对象约束
     * @param poQuery
     * @return
     */
    public List<Vo> list(Po poQuery, IDataObject dataObject){
        assertNotNull(dataObject,"数据对象编码不能为空");
        return list(poQuery, parseConstraint(dataObject));
    }

    /**
     * 不分页查询，重载数据对象约束,可以带额外参数
     * @param poQuery
     * @param dataObject
     * @return
     */
    public List<Vo> list(BasePoQuery<Po> poQuery,IDataObject dataObject){
        assertNotNull(dataObject,"数据对象编码不能为空");
        return list(poQuery, parseConstraint(dataObject));
    }
    /**
     * 不分页查询，重载数据对象约束
     * @param poQuery
     * @return
     */
    public List<Vo> list(Po poQuery,List<ConstraintCompiledContent> constraintContent){
        assertNotNull(constraintContent,"约束内容不能为空");
        List list = getService().list(poQuery,constraintContent);
        return posToVos(list);
    }

    /**
     * 不分页查询，重载数据对象约束,可以带额外参数
     * @param poQuery
     * @param constraintContent
     * @return
     */
    public List<Vo> list(BasePoQuery<Po> poQuery,List<ConstraintCompiledContent> constraintContent){
        assertNotNull(constraintContent,"约束内容不能为空");
        List list = getService().list(poQuery,constraintContent);
        return posToVos(list);
    }


    /**
     * 解析数据范围约束
     * @param dataObjectCode
     * @return
     */
    protected List<ConstraintCompiledContent> parseConstraint(String dataObjectCode){
        if (baseDataConstraintHelper == null) {
            throw new RBaseException("baseDataConstraintHelper is null,check config please");
        }
        return baseDataConstraintHelper.doParseConstraint(dataObjectCode, AbstractLoginUser.getLoginUser());
    }

    /**
     * 解析数据范围约束
     * @param dataObjectCode
     * @return
     */
    protected List<ConstraintCompiledContent> parseConstraint(IDataObject dataObjectCode){
        return parseConstraint(dataObjectCode.dataObjectCode());
    }
    /**
     * 启动默认的数据对象编码
     */
    protected void enableDefaultDataObject(){
        // 记得用完从线程变量删除
        ThreadContextTool.put(enableDefaultDataObjectKey,true);
    }

    /**
     * 禁用默认的数据对象编码
     */
    protected void disableDefaultDataObject(){
        // 记得用完从线程变量删除
        ThreadContextTool.put(enableDefaultDataObjectKey,false);
    }

    /**
     * 获取设置的启用变量
     * @return
     */
    protected Object getEnableDefaultDataObjectKeyValue(){
        return ThreadContextTool.get(enableDefaultDataObjectKey);
    }
    /**
     * 重写该方法以全局开启默认数据对象
     * @return
     */
    protected boolean isEnableDefaultDataObject(){
        boolean r = false;
        if (getEnableDefaultDataObjectKeyValue()!= null) {
            r = (boolean) getEnableDefaultDataObjectKeyValue();
            ThreadContextTool.remove(enableDefaultDataObjectKey);
        }
        return r;
    }
    /**
     * 默认的数据对象编码
     * @return
     */
    protected String defaultDataObjectCode(){
        throw new RuntimeException("必须重写grape.common.rest.mvc.BaseController.defaultDataObjectCode方法,以设置默认的数据对象编码");
    }
}
